package net.djpowell.liverepl.client; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.logging.Logger; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; public class Main { // bind to localhost to keep things more secure public static final InetAddress LOCALHOST; static { try { LOCALHOST = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); } catch (UnknownHostException e) { throw new RuntimeException(e); } } private static int getFreePort(InetAddress host) { // open a server socket on a random port, then close it // so that we can tell the server to listen on that port // and we will know which port to connect to try { ServerSocket server = new ServerSocket(0, 0, host); int port = server.getLocalPort(); server.close(); return port; } catch (IOException e) { throw new RuntimeException(e); } } private static void listPids() { System.out.println(); System.out.println("liverepl"); System.out.println("Usage: liverepl <pid>"); System.out.println(); System.out.println("List of Java processes"); System.out.format("%1$-6s %2$.60s%n", "pid", "Details"); for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { System.out.format("%1$-6s %2$.60s%n", vmd.id(), vmd.displayName()); } } public static void main(String[] args) throws Exception { // Usage: <clojurepath> <agentjarpath> <serverjarpath> <jvmpid> <classloaderid> if (args.length < 4) { listPids(); System.exit(0); } String clojurepath = args[0]; String agentpath = args[1]; String serverpath = args[2]; String pid = args[3]; String classLoaderId; if (args.length < 5) { classLoaderId = "L"; System.out.println(); System.out.println("List of ClassLoaders for process #" + pid); } else { classLoaderId = args[4]; } TRC.fine("Attaching to pid: " + pid); final VirtualMachine vm = VirtualMachine.attach(pid); int port = getFreePort(LOCALHOST); // start the agent, which will create the server socket, then return String agentArgs = String.valueOf(port) + "\n" + clojurepath + "\n" + serverpath + "\n" + classLoaderId; vm.loadAgent(agentpath, agentArgs); boolean listClassLoaders = "L".equals(classLoaderId); // start the code that will connect to the server socket Console.main(LOCALHOST, port, !listClassLoaders); // the server will shutdown when the client disconnects Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { vm.detach(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } }); } private static final Logger TRC = Logger.getLogger(Main.class.getName()); }